package org.wevil.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.wevil.entity.news.News;
import org.wevil.entity.view.ViewNews;
import org.wevil.enumu.NewsStatusEnum;
import org.wevil.params.add.NewsAP;
import org.wevil.params.page.NewsPP;
import org.wevil.params.update.NewsUP;
import org.wevil.repo.NewsRepo;
import org.wevil.repo.ViewNewsRepo;
import org.wevil.service.sys.ResService;
import org.wevil.service.sys.RoleService;
import org.wevil.util.BeanUtil;
import org.wevil.util.DateUtil;
import org.wevil.util.ListUtil;
import org.wevil.util.page.PageResult;
import org.wevil.util.page.PageResultUtil;
import org.wevil.vo.detail.NewsDetailVo;
import org.wevil.vo.list.NewsListVo;
import org.wevil.vo.stat.BarVo;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Transactional
public class NewsService {

    @Resource
    NewsRepo repo;
    @Resource
    NewsRepo newsRepo;
    @Resource
    ViewNewsRepo viewNewsRepo;
    @Resource
    ResService resService;
    @Resource
    SubjectService subjectService;
    @Resource
    RoleService roleService;

    /**
     * 添加新闻
     */
    public void add(NewsAP param, Integer userId) {
        // 保存新闻
        News news = new News();
        BeanUtil.copyPropIgnoreNull(param, news);
        news.setCreateId(userId);
        news.setUi(userId);
        news.setCreateTime(new Timestamp(System.currentTimeMillis()));
        news.setNewsStatus(NewsStatusEnum.WAIT_AUDIT.getCode());
        // 保存附件
        List<Integer> affixIdsList = param.getAffixIds();
        if (affixIdsList != null && affixIdsList.size() > 0) {
            String affixIds = ListUtil.intList2Str(affixIdsList);
            news.setAffixIds(affixIds);
        }
        repo.save(news);
    }

    /**
     * 删除新闻
     */
    public void delete(Integer newsId) {
        // 删除新闻
        repo.deleteById(newsId);
    }

    /**
     * 更新新闻
     */
    public void update(NewsUP param, Integer userId) {
        Integer id = param.getId();
        // 更新新闻
        News news = new News();
        BeanUtil.copyPropIgnoreNull(param, news);
        // 更新用户
        news.setUpdateId(userId);
        news.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        // 保存附件
        List<Integer> affixIdsList = param.getAffixIds();
        if (affixIdsList != null && affixIdsList.size() > 0) {
            String affixIds = ListUtil.intList2Str(affixIdsList);
            news.setAffixIds(affixIds);
        }
        repo.update(news);
    }

    /**
     * 分页查询
     * @param param 请求参数
     * @param userId 当前用户id
     * @param sub 查询结果是否包含子栏目数据
     */
    public PageResult<NewsListVo> page(NewsPP param, Integer userId, boolean sub) {
        Integer current = param.getCurrent();
        Integer pageSize = param.getSize();
        // 要查询的字段
        String title = param.getTitle();
        Integer status = param.getStatus();
        Integer subjectId = param.getSubjectId();
        String username = param.getUsername();
        Timestamp tsStart = DateUtil.convertStr2Timestamp("2000-01-01 00:00:00");
        Timestamp tsEnd = DateUtil.convertStr2Timestamp("2099-01-01 00:00:00");
        List<String> tsScale = param.getTsScale();
        if(tsScale!=null && tsScale.size()==2){
            tsStart = DateUtil.convertStr2Timestamp(tsScale.get(0));
            tsEnd = DateUtil.convertStr2Timestamp(tsScale.get(1));
        }
        Timestamp finalTsStart = tsStart;
        Timestamp finalTsEnd = tsEnd;
        Specification<ViewNews> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.isNotBlank(title)) {
                predicates.add(cb.like(root.get("title").as(String.class), "%" + title + "%"));
            }
            if (StringUtils.isNotBlank(username)) {
                predicates.add(cb.like(root.get("username").as(String.class), "%" + username + "%"));
            }
            if (status != null) {
                predicates.add(cb.equal(root.get("newsStatus").as(Integer.class), status));
            }
            if (subjectId != null) {
                if(sub){
                    List<Integer> ids = subjectService.getSubId(subjectId);
                    ids.add(subjectId);
                    predicates.add(root.get("subjectId").in(ids));
                }else{
                    predicates.add(cb.equal(root.get("subjectId").as(Integer.class), subjectId));
                }
            }else{
                if(userId != null && userId != 1){
                    List<Integer> roleIds = roleService.getRoleIdListByUserId(userId);
                    List<Integer> subjectIds = subjectService.getSubjectIdsByRoleIds(roleIds);
                    predicates.add(root.get("subjectId").in(subjectIds));
                }
            }
            predicates.add(cb.between(root.get("ts"), finalTsStart, finalTsEnd));
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "ts");
        Pageable pageable = PageRequest.of(current - 1, pageSize, sort);
        Page<ViewNews> page = viewNewsRepo.findAll(specification, pageable);
        // 封装返回数据
        Iterator<ViewNews> iterator = page.iterator();
        List<NewsListVo> voList = new ArrayList<>();
        while (iterator.hasNext()) {
            ViewNews next = iterator.next();
            NewsListVo dto = new NewsListVo();
            BeanUtil.copyPropIgnoreNull(next, dto);
            // 设置封面
            Integer coverId = next.getCoverId();
            if (coverId != null) {
                String path = resService.getPath(coverId);
                dto.setCoverUri(path);
            }
            voList.add(dto);
        }
        // 返回
        long total = viewNewsRepo.count(specification);
        return PageResultUtil.generateResult(voList, current, pageSize, total);
    }

    /**
     * 新闻详情
     */
    public NewsDetailVo detail(Integer id) {
        NewsDetailVo vo = new NewsDetailVo();
        Optional<ViewNews> optional = viewNewsRepo.findById(id);
        if (optional.isPresent()) {
            ViewNews entity = optional.get();
            // 获取一次，添加一次点击数
            addClick(id);
            // 设置封面
            Integer coverId = entity.getCoverId();
            if (coverId != null) {
                String path = resService.getPath(coverId);
                vo.setCoverUri(path);
            }
            // 设置附件
            String affixIds = entity.getAffixIds();
            if(affixIds!=null && affixIds.length()>0){
                List<Integer> affixIdList = ListUtil.str2intList(affixIds);
                List<String> paths = resService.getPaths(affixIdList);
                vo.setAffixUris(paths);
            }
            BeanUtil.copyPropIgnoreNull(entity, vo);
        }
        return vo;
    }

    /**
     * 审核通过 审核通过：待审核 -> 待发布
     */
    public void auditPass(Integer newsId, Integer userId) {
        Optional<News> optional = repo.findById(newsId);
        if (optional.isPresent()) {
            News news = optional.get();
            news.setAuditId(userId);
            news.setAuditTime(new Timestamp(System.currentTimeMillis()));
            news.setNewsStatus(NewsStatusEnum.WAIT_PUBLIC.getCode());
            news.setUi(userId);
            repo.update(news);
        }
    }

    /**
     * 审核不通过：待审核 -> 未通过
     */
    public void noPass(Integer newsId, Integer userId) {
        Optional<News> optional = repo.findById(newsId);
        if (optional.isPresent()) {
            News news = optional.get();
            news.setAuditId(userId);
            news.setAuditTime(new Timestamp(System.currentTimeMillis()));
            news.setNewsStatus(NewsStatusEnum.NO_PASS.getCode());
            news.setUi(userId);
            repo.update(news);
        }
    }

    /**
     * 发布：待发布 -> 已发布
     */
    public void pub(Integer newsId, Integer userId) {
        Optional<News> optional = repo.findById(newsId);
        if (optional.isPresent()) {
            News news = optional.get();
            news.setPublishId(userId);
            news.setPublishTime(new Timestamp(System.currentTimeMillis()));
            news.setNewsStatus(NewsStatusEnum.HAS_PUBLIC.getCode());
            news.setUi(userId);
            repo.update(news);
        }
    }

    /**
     * 撤回：已发布 -> 已撤回
     */
    public void back(Integer newsId, Integer userId) {
        Optional<News> optional = repo.findById(newsId);
        if (optional.isPresent()) {
            News news = optional.get();
            news.setUpdateId(userId);
            news.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            news.setNewsStatus(NewsStatusEnum.ROLL_BACK.getCode());
            news.setUi(userId);
            repo.update(news);
        }
    }

    /**
     * 重新提交：已撤回 -> 待审核 | 未通过 -> 待审核
     */
    public void reSubmit(Integer newsId, Integer userId) {
        Optional<News> optional = repo.findById(newsId);
        if (optional.isPresent()) {
            News news = optional.get();
            news.setUpdateId(userId);
            news.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            news.setNewsStatus(NewsStatusEnum.WAIT_AUDIT.getCode());
            news.setUi(userId);
            repo.update(news);
        }
    }

    /** 浏览次数 */
    public Integer viewCount() {
        return newsRepo.sumByClick();
    }

    /** 各个栏目信息统计 */
    public List<BarVo> subjectNewsCount() {
        List<BarVo> barVos = viewNewsRepo.countBySubject();
        // 排序
        barVos = barVos.stream()
                .filter(bar -> bar.getName() != null)
                .sorted((bar1, bar2) -> bar2.getValue().compareTo(bar1.getValue())).collect(Collectors.toList());
        // 截取前七条
        return barVos.subList(0, 7);
    }

    // -------------------------------------------------私有方法-------------------------------------//

    /**
     * 点击数+1
     *
     * @since 2022/3/16
     */
    private void addClick(Integer newsId) {
        Optional<News> opt = newsRepo.findById(newsId);
        if (opt.isPresent()) {
            News entity = opt.get();
            Integer newsClick = entity.getClick();
            if (newsClick == null) { // 第一次
                newsClick = 1;
            } else {
                newsClick += 1;
            }
            entity.setClick(newsClick);
            newsRepo.update(entity);
        }
    }

    /** 总信息发布条数 */
    public Integer newsPublishCount() {
        long count = newsRepo.countByPublish();
        return (int)count;
    }

    /** 获取所有新闻的创建用户 */
    public List<String> listCreateUser() {
        return viewNewsRepo.findAllUsername();
    }
}
